#include <gfx/dx12/core.h>
#include <utl/memory.h>
#include <array>

namespace primal
{
	namespace gfx
	{
		constexpr std::array<DxLevel, 11> level_mapping
		{
			DxLevel{D3D_FEATURE_LEVEL_1_0_CORE,L"D3D_FEATURE_LEVEL_1_0_CORE"},
			DxLevel{D3D_FEATURE_LEVEL_9_1,L"D3D_FEATURE_LEVEL_9_1"},
			DxLevel{D3D_FEATURE_LEVEL_9_2,L"D3D_FEATURE_LEVEL_9_2"},
			DxLevel{D3D_FEATURE_LEVEL_9_3,L"D3D_FEATURE_LEVEL_9_3"},
			DxLevel{D3D_FEATURE_LEVEL_10_0,L"D3D_FEATURE_LEVEL_10_0"},
			DxLevel{D3D_FEATURE_LEVEL_10_1,L"D3D_FEATURE_LEVEL_10_1"},
			DxLevel{D3D_FEATURE_LEVEL_11_0,L"D3D_FEATURE_LEVEL_11_0"},
			DxLevel{D3D_FEATURE_LEVEL_11_1,L"D3D_FEATURE_LEVEL_11_1"},
			DxLevel{D3D_FEATURE_LEVEL_12_0,L"D3D_FEATURE_LEVEL_12_0"},
			DxLevel{D3D_FEATURE_LEVEL_12_1,L"D3D_FEATURE_LEVEL_12_1"},
			DxLevel{D3D_FEATURE_LEVEL_12_2,L"D3D_FEATURE_LEVEL_12_2"},
		};

		struct
		{
			ComPtr<IDXGIFactoryN> fac;
			ComPtr<IDXGIAdapterN> ada;
			ComPtr<ID3D12DeviceN> dev;
			utl::unique_ptr<DirectQueue> directq;
			utl::unique_ptr<CopyQueue> copyq;
			utl::unique_ptr<SRVDescriptorHeap> shader_resource_view_desc_heap;
			utl::unique_ptr<RTVDescriptorHeap> render_target_view_desc_heap;
			utl::unique_ptr<DSVDescriptorHeap> depth_stencil_view_desc_heap;
			DxLevel level;
			bool is_init;
		}globle{};
	}
}

#ifdef _DEBUG
#define PRIMAL_DXGI_FLAG 1u
#else
#define PRIMAL_DXGI_FLAG 0u
#endif // _DEBUG

bool primal::gfx::initialize()
{
	if (globle.is_init)
	{
		return globle.is_init;
	}

	//do initialize
	{
#ifdef _DEBUG
		{
			ComPtr<ID3D12Debug> debug_interface{};
			DXASSERT(D3D12GetDebugInterface(IID_PPV_ARGS(debug_interface.GetAddressOf())));
			debug_interface->EnableDebugLayer();
		}
#endif // _DEBUG
		DXASSERT(CreateDXGIFactory2(PRIMAL_DXGI_FLAG, IIDPPV(globle.fac)));
		DXASSERT(globle.fac->EnumAdapterByGpuPreference(0u, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IIDPPV(globle.ada)));
		for (auto item = level_mapping.rbegin(); item != level_mapping.rend(); item++)
		{
			if (D3D12CreateDevice(globle.ada.Get(), item->level, IIDPPV(globle.dev)) == S_OK)
			{
				globle.level = *item;
				break;
			}
		}
#ifdef _DEBUG
		{
			ComPtr<ID3D12InfoQueue> info_queue{};
			globle.dev.As(&info_queue);
			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true);
			info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
		}
#endif // _DEBUG
		globle.directq = utl::make_unique<DirectQueue>(device());
		globle.copyq = utl::make_unique<CopyQueue>(device());

		globle.shader_resource_view_desc_heap = utl::make_unique<SRVDescriptorHeap>(device());
		globle.render_target_view_desc_heap = utl::make_unique<RTVDescriptorHeap>(device());
		globle.depth_stencil_view_desc_heap = utl::make_unique<DSVDescriptorHeap>(device());
	}

	globle.is_init = true;
	return globle.is_init;
}

void primal::gfx::shutdown()
{
	if (globle.is_init)
	{
		globle.is_init = false;
	}
}

ID3D12Resource* primal::gfx::create_rtv_srv(u32 width, u32 height, descriptor_handle& rtv, descriptor_handle& srv)
{	
	return nullptr;
}

primal::gfx::ID3D12DeviceN* primal::gfx::device()
{
	return globle.dev.Get();
}

primal::gfx::IDXGIFactoryN* primal::gfx::factory()
{
	return globle.fac.Get();
}

primal::gfx::DirectQueue& primal::gfx::direct_queue()
{
	return *globle.directq.get();
}

primal::gfx::CopyQueue& primal::gfx::copy_queue()
{
	return *globle.copyq.get();
}

primal::gfx::SRVDescriptorHeap& primal::gfx::srv()
{
	return *globle.shader_resource_view_desc_heap.get();
}

primal::gfx::RTVDescriptorHeap& primal::gfx::rtv()
{
	return *globle.render_target_view_desc_heap.get();
}

primal::gfx::DSVDescriptorHeap& primal::gfx::dsv()
{
	return *globle.depth_stencil_view_desc_heap.get();
}

primal::gfx::DxLevel primal::gfx::get_level()
{
	return globle.level;
}

